package jp.sourceforge.akjrcp.util.directorybased;

import com.akjava.lib.standards.process.ProcessPrinter;
import com.akjava.lib.standards.process.SimpleProcessPrinter;

import java.io.File;
import java.io.IOException;

import jp.sourceforge.akjrcp.ui.DirectryBasedExecuterListener;
import jp.sourceforge.akjrcp.util.FileNameUtils;
import jp.sourceforge.akjrcp.util.PathnameUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public abstract class DirectoryBasedCommandExecuter {
public static Log log = LogFactory.getLog(DirectoryBasedCommandExecuter.class);

protected DirectryBasedExecuterListener executeListener;

protected String exportDir;
protected boolean sameDirectory;
protected boolean backup;

protected boolean cancelling;


protected File inputParentDirectory;

protected File currentOutputFile; //current Processing file

protected String outputExtension;// 

protected boolean renameSameFile=true;
public void execute(File files[]){
	if(executeListener!=null){
		executeListener.beforeConvert();
	}
	
	for (int i = 0; i < files.length; i++) {
		inputParentDirectory=files[i];//for keep directory path
		execute(files[i]);
	}
	
	if(executeListener!=null){
		executeListener.afterConvert();
	}
}

private void execute(File file){
	//TODO //filename filter?
	if(file.isDirectory()){
		
		if(cancelling){
			log.info("cancel "+file);
			return;
		}
		
		String list[]=file.list();
		if(list!=null){
			for (int i = 0; i < list.length; i++) {
				execute(new File(file,list[i]));
			}
		}
	}else{
		//TODO should check file.
		inExecute(file);
	}
}

public void cancel(){
	log.info("called cancel");
	synchronized(this){
		log.info("cancell:"+System.currentTimeMillis());
		cancelling=true;
		}
	if(getProcess()!=null){
		getProcess().destroy();
		log.info("maybe destroyed:"+getProcess());
	}
	
}

public Process getProcess(){
	if(currentCommand!=null){
		return currentCommand.getProcess();
	}else{
		return null;
	}
}

private File toOutputFile(File file){
	String outputDir=file.getParent();
	String outputPath;
	if(!sameDirectory){
		outputDir=exportDir;
		String remain=PathnameUtils.getRemainPath(inputParentDirectory.getParent(),PathnameUtils.changeExtension(file.getAbsolutePath(),outputExtension));
		log.info("remain:"+remain);
		outputPath=outputDir+"\\"+remain;
	}else{
		//same directory just rename it.
		outputPath=outputDir+"\\"+PathnameUtils.changeExtension(file.getName(),outputExtension);
	}
	
	log.info("saveto:"+outputPath);
	
	
	//if input and output same faild and return;
	if(file.getAbsolutePath().equals(outputPath)){
		if(renameSameFile){
		File newFile=FileNameUtils.makeBackupFile(file,"new");
		outputPath=newFile.getAbsolutePath();
		}else{
		//
		if(executeListener!=null){
			executeListener.faildConvert(file,DirectryBasedExecuterListener.ERROR_SAME_FILE);
			return null;
		}
		}
	}
	
	// check file can write;
	boolean overwrite=false;
	File tmpOutput=new File(outputPath);
	

	
	if(tmpOutput.exists() && !tmpOutput.canWrite()){
		//if the file is ready only
		if(executeListener!=null){
			executeListener.faildConvert(file,DirectryBasedExecuterListener.ERROR_CANT_OVERWRITE);
			return null;
		}
	}
	
	//ask overwrite
	if(!backup){
		
	if(tmpOutput.exists()){
		
		if(executeListener!=null){
		overwrite=executeListener.askOverwrite(tmpOutput);
		if(!overwrite){
			if(executeListener!=null){
			executeListener.faildConvert(file,DirectryBasedExecuterListener.ERROR_DONT_OVERWRITE);
			}
			return null;
		}
		}
		boolean deleted=tmpOutput.delete();//delete first.
		if(!deleted){
			if(executeListener!=null){
				executeListener.faildConvert(file,DirectryBasedExecuterListener.ERROR_CANT_OVERWRITE);
				return null;
			}
		}
	}
	}else{
		//do backup
		File backUpFile=FileNameUtils.makeBackupFile(tmpOutput,"backup");
		if(backUpFile.exists()){
			boolean r=backUpFile.delete();
			if(!r){
				log.warn("delete faild "+backUpFile);
				if(executeListener!=null){
					executeListener.faildConvert(file,DirectryBasedExecuterListener.ERROR_CANT_BACK+" "+backUpFile);
					}
				return null;
			}else{
				log.info("delete success "+backUpFile);
			}
		}
		if(tmpOutput.exists()){
		boolean r=tmpOutput.renameTo(backUpFile);
		if(!r){
			log.warn("backup faild "+tmpOutput);
			if(executeListener!=null){
				executeListener.faildConvert(file,DirectryBasedExecuterListener.ERROR_CANT_BACK+" "+tmpOutput);
				}
			return null;
		}else{
			log.info("backup success "+tmpOutput);
		}
		}
	}
	
	return tmpOutput;
}



protected abstract DirectoryBasedCommand[] toCommand(File input,File output);

protected ProcessPrinter processPrinter=new SimpleProcessPrinter();

protected DirectoryBasedCommand currentCommand;
private void inExecute(File file){
	//check somethng.
	if(cancelling){
		log.info("cancel "+file);
		return;
	}
	
	
	File tmpOutput=toOutputFile(file);
	if(tmpOutput==null){
		return;//do somethin faild;
	}
	
	setCurrentOutputFile(tmpOutput);
	
	
	
	
	
	if(getProcess()!=null){
		//anyway old process must die.
		getProcess().destroy();
	}
	
	
	
	
	//because threa some time rag to know cancell
	if(cancelling){
		log.info("cancel "+file);
		return;
	}
	//finally make file dirs.
	tmpOutput.getParentFile().mkdirs();
	
		DirectoryBasedCommand commands[]=toCommand(file,tmpOutput);
		if(commands==null){
			log.warn("null command");
			if(executeListener!=null){
				executeListener.faildConvert(file,"");
			}
			return;
		}
		
		if(executeListener!=null){
			executeListener.startConvert(file);
		}
		
			
		
		
		
		try {
			for(int i=0;i<commands.length;i++){
				if(cancelling){
					log.info("canceled "+file);
					throw new IOException("cancelled");
					}
			currentCommand=commands[i];
			
			
			
				if(!cancelling){
					beforeExecuteCommand(currentCommand);
					currentCommand.setProcessPrinter(processPrinter);
					int result=currentCommand.exec();
					processReturnValue(result);
					if(result!=0){
						throw new IOException("exec faild");
					}
					afterExecuteCommand(currentCommand);
				
				}else{
				throw new IOException("cancelled");
				}
		}
		} catch (Exception e) {
			if(getProcess()!=null){
			getProcess().destroy();
			}
			if(executeListener!=null){
				executeListener.faildConvert(file,e.getMessage());
				}
			e.printStackTrace();
		}finally{
			inFinallyAction(file,tmpOutput);
		}
		
	//call finish
	if(executeListener!=null){
		executeListener.endConvert(file);
		}
	
	currentCommand=null;
	//cancelling=false; damm.this time cancell all only.
}
abstract protected void afterExecuteCommand(DirectoryBasedCommand command);
abstract protected void beforeExecuteCommand(DirectoryBasedCommand command);
abstract protected void processReturnValue(int value);
abstract protected void inFinallyAction(File file,File output);

public class TestMonitor implements DirectryBasedExecuterListener{

	public void beforeConvert() {
		// TODO Auto-generated method stub
		
	}

	public void afterConvert() {
		// TODO Auto-generated method stub
		
	}

	public void startConvert(File file) {
		// TODO Auto-generated method stub
		
	}

	public void endConvert(File file) {
		// TODO Auto-generated method stub
		
	}

	public void faildConvert(File file, String message) {
		log.info("faild:"+file);
	}

	public void printProcess(String line) {
		// TODO Auto-generated method stub
		
	}

	public boolean askOverwrite(File output) {
		// TODO Auto-generated method stub
		return false;
	}
	
}

public static String toDebugArray(String[] array){
	String v="";
	for (int i = 0; i < array.length; i++) {
		v+="\""+array[i]+"\""+" ";
	}
	return v+"\n";
}

public boolean isBackup() {
	return backup;
}

public void setBackup(boolean backup) {
	this.backup = backup;
}

public DirectryBasedExecuterListener getExecuteListener() {
	return executeListener;
}

public void setExecuteListener(DirectryBasedExecuterListener executeListener) {
	this.executeListener = executeListener;
}

public String getExportDir() {
	return exportDir;
}

public void setExportDir(String exportDir) {
	this.exportDir = exportDir;
}



public boolean isSameDirectory() {
	return sameDirectory;
}

public void setSameDirectory(boolean sameDirectory) {
	this.sameDirectory = sameDirectory;
}



public class ErrorCatchProcessPrinter implements ProcessPrinter{
	public void printOutput(String output) throws IOException {
		print(output);
	}

	public void printError(String error)  throws IOException{
		print(error);
	}

	private void print(String line) throws IOException{
		if(executeListener!=null){
			executeListener.printProcess(line);
			}
		log.debug(line);
		
		if(line.indexOf(DirectryBasedExecuterListener.ERROR_NOTSUPPORTED_VIDEO)!=-1){
			//cancel();
			//ignore only this time.
			
			if(line.indexOf("flv")!=-1){
				log.info("flv is sensitive");
				
				
			}else{
				//please don't use old ffmpeg.exe
				//throw new IOException(DirectryBasedExecuterListener.ERROR_NOTSUPPORTED_VIDEO);
				log.info("unsupported:");
				}
			}
		
	}
	
}

public File getCurrentOutputFile() {
	return currentOutputFile;
}
public void setCurrentOutputFile(File currentOutputFile) {
	this.currentOutputFile = currentOutputFile;
}

public String getOutputExtension() {
	return outputExtension;
}

public void setOutputExtension(String outputExtension) {
	this.outputExtension = outputExtension;
}

public ProcessPrinter getProcessPrinter() {
	return processPrinter;
}

public void setProcessPrinter(ProcessPrinter processPrinter) {
	this.processPrinter = processPrinter;
}

}
